package key.w8.boot.service;

import com.k8.common.constants.CommonKeyConstants;
import com.k8.common.event.EventDeployer;
import com.k8.common.executor.ExecutorRepository;
import com.k8.common.factroy.BeanFactory;
import com.k8.common.factroy.loader.ExtensionDirector;
import com.k8.common.util.StrUtil;
import com.k8.registry.config.ServiceBase;
import com.k8.rpc.config.ProtocolConfig;
import com.k8.rpc.expoter.ExporterListener;
import com.k8.rpc.metadata.AppMetaData;
import com.k8.rpc.metadata.ProtocolMetadata;
import com.k8.rpc.url.URL;
import com.k8.rpc.util.ConfigUtil;
import com.k8.rpc.util.URLUtils;
import key.w8.boot.contenxt.ServiceContext;
import key.w8.boot.properties.ProviderAutoConfigurationProperties;
import key.w8.boot.properties.Registry;
import key.w8.boot.util.ConvertURLUtil;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

/**
 * @Author: k8
 * @CreateTime: 2025-07-28
 * @Version: 1.0
 */
public class ServiceFactory implements ApplicationContextAware {
    private final List<ServiceContext> serviceContexts;
    private ProviderAutoConfigurationProperties k8ConfigProperties;

    private BeanFactory beanFactory;
    private ApplicationContext applicationContext;

    public ServiceFactory(List<ServiceContext> serviceContexts) {
        this.serviceContexts = serviceContexts;
    }

    public void init() {
        URL systemConfigUrl = ConfigUtil.mapConfigToConstants(k8ConfigProperties);
        String appName = k8ConfigProperties.getAppName();
        if (StrUtil.isEmpty(appName)) throw new IllegalStateException("The appName has not been configured.");
        systemConfigUrl.setSide(CommonKeyConstants.PROVIDER);
        ExtensionDirector extensionDirector = beanFactory.getExtensionDirector();
        systemConfigUrl.setExtensionAccessor(beanFactory.getExtensionDirector());
        ExecutorRepository defaultExecutorRepository = extensionDirector.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
        ExecutorService executorService = defaultExecutorRepository.getExecutorService(systemConfigUrl);
        EventDeployer eventDeployer = new EventDeployer(executorService);
        beanFactory.registryBean(eventDeployer);
        eventDeployer.addListener(new ExporterListener());
        for (ServiceContext serviceContext : serviceContexts) {
            List<String> protocols = serviceContext.getProtocols();
            Integer[] ports = serviceContext.getPorts();
            if (ports.length != protocols.size())
                throw new IllegalStateException("Ports and protocols must be matched one by one.");
            if (CollectionUtils.isEmpty(protocols)) {
                String protocol = k8ConfigProperties.getProtocol();
                Integer port = k8ConfigProperties.getPort();
                if (StrUtil.isEmpty(protocol) || port == null)
                    throw new IllegalStateException("At least one protocol or port is required.");
                protocols = List.of(protocol);
                ports = new Integer[]{port};
            }
            int index = 0;
            List<ProtocolConfig> protocolConfigList = new ArrayList<>(protocols.size());
            for (String protocol : protocols) {
                //这里应该交给定制化配置
                ProtocolConfig protocolConfig = new ProtocolConfig();
                protocolConfig.setName(protocol);
                protocolConfig.setPort(ports[index++]);
                protocolConfigList.add(protocolConfig);
            }
            List<Registry> registries = k8ConfigProperties.getRegistries();
            if (CollectionUtils.isEmpty(registries)) {
                throw new IllegalStateException("At least one registration center is required.");
            }
            List<URL> registryConfigUrls = ConvertURLUtil.getRegistryConfigUrls(registries, systemConfigUrl, CommonKeyConstants.PROVIDER);
            List<URL> protocolUrls = protocolConfigList.stream().map(protocolConfig1 -> {
                return URLUtils.buildUrlByProtocolConfig(protocolConfig1, systemConfigUrl);
            }).collect(Collectors.toList());
            ServiceBase<Object> serviceBase = new ServiceBase<>(beanFactory, systemConfigUrl, registryConfigUrls, protocolUrls);
            serviceBase.setInstance(serviceContext.getInstance());
            serviceBase.setVersion(serviceContext.getVersion());
            serviceBase.setGroup(serviceContext.getGroup());
            serviceBase.setType(serviceContext.getType());
            serviceBase.export();
            Map<String, ProtocolMetadata> protocolMetadataMap = new HashMap<>();
            protocolConfigList.stream()
                    .map(protocolConfig -> {
                        ProtocolMetadata protocolMetadata = new ProtocolMetadata();
                        protocolMetadata.setProtocol(protocolConfig.getName())
                                .setPort(protocolConfig.getPort());
                        return protocolMetadata;
                    }).forEach(protocolMetadata -> {
                        protocolMetadataMap.put(protocolMetadata.getProtocol(), protocolMetadata);
                    });
            AppMetaData appMetaData = new AppMetaData();
            appMetaData.setHost(k8ConfigProperties.getHost())
                    .setPort(k8ConfigProperties.getPort())
                    .setProtocols(protocolMetadataMap)
                    //todo 修改id的方式
                    .setServiceInstanceId(UUID.randomUUID().getLeastSignificantBits());
            serviceBase.exportApp(appMetaData);
        }
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        this.k8ConfigProperties = applicationContext.getBean(ProviderAutoConfigurationProperties.class);
        this.beanFactory = applicationContext.getBean("providerBeanFactory", BeanFactory.class);
    }
}
